package de.bazinga.rotw.weapon.pistol.bullet;

import java.util.Collection;
import java.util.Vector;

import com.jme.bounding.BoundingBox;
import com.jme.intersection.PickData;
import com.jme.intersection.PickResults;
import com.jme.intersection.TrianglePickResults;
import com.jme.math.Ray;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
import com.jmex.game.state.GameStateManager;
import com.jmex.terrain.TerrainBlock;

import de.bazinga.rotw.collision.CollidableObjects;
import de.bazinga.rotw.effects.ParticleEffects;
import de.bazinga.rotw.game.RotwGameState;
import de.bazinga.rotw.network.BasicSyncManager;
import de.bazinga.rotw.network.Server;
import de.bazinga.rotw.network.message.HitMessage;
import de.bazinga.rotw.network.message.NewBulletMessage;
import de.bazinga.rotw.person.OpponentList;
import de.bazinga.rotw.person.Person;
import de.bazinga.rotw.util.GlobalSettings;
import de.bazinga.rotw.vehicle.Vehicle;
import de.bazinga.rotw.weapon.Pistol;

public class BulletController extends Controller {
	private static final long serialVersionUID = 1L;
	
	private Bullet bullet = null;
	private Vector3f direction = null;
	
	private float speed = 0;
	
	private float maxDistance = GlobalSettings.MAXBULLETDISTANCE;
	
	private int topthirdDamage = Pistol.TOPTHIRD_DAMAGE;
	private int middlethirdDamage = Pistol.MIDDLETHIRD_DAMAGE;
	private int bottomthirdDamage = Pistol.BOTTOMTHIRD_DAMAGE;
	
	
	
	public BulletController(Bullet bullet, Vector3f direction, float speed) {
		this.bullet = bullet;
		this.direction = direction;
		
		this.speed = speed;
	}

	@Override
	public void update(float tpf) {
		// Is Active?
		if(!bullet.isActive()) return ;
		
		
		bullet.setLocalTranslation(
				bullet.getLocalTranslation().addLocal(
						direction.mult(tpf*speed)));
		
		float distanceTraveled = bullet.getStartPosition().distance(bullet.getLocalTranslation());
		
		if(distanceTraveled > maxDistance) {
			doExplosion();
			bullet.setActive(false);
			//((TuxGameState)States.getInstance().getState("gameState")).addBulletToRemove(bullet);
			return ;
		}
		
		

		BasicSyncManager syncManager = 
			((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getSyncManager();
		
		// Only check if this is the server or if the bullet traveled at least 1f, so we dont hit ourselves
		if(syncManager instanceof Server || distanceTraveled > 3f) {
			// Check Opponents
			Collection<Person> persons = OpponentList.getInstance().getOpponents().values();
			//persons.remove(bullet.getPlayer()); // Cant hit yourself
			for(Person opp:persons) {
				if(opp != null && opp != bullet.getPlayer() && opp.hasCollision(bullet, false)) {
					doExplosion();
	
					
					
					// Tell Opponent that he got hit!
					if(syncManager instanceof Server) {
						
						// Calculate the hit-height
						
						float hitHeight = bullet.getLocalTranslation().getY() - opp.getLocalTranslation().getY();
						
						int damage = 0;
						
						if(hitHeight > ((2f/3f) * (2*((BoundingBox)opp.getWorldBound()).yExtent))) {
							damage = topthirdDamage;
						}else if(hitHeight > (1f/3f) * (2*((BoundingBox)opp.getWorldBound()).yExtent)) {
							damage = middlethirdDamage;
						}else{
							damage = bottomthirdDamage;
						}
						
						
						if(opp.getTeam() != bullet.getPlayer().getTeam()) {
							// Tell the opponent that he got hit by bullet.getPlayer();
							// only the Server should do that! the clients just display the bullet
							opp.hit(bullet.getPlayer(), damage);
							
							
							HitMessage hitMessage = new HitMessage();
							hitMessage.setPlayerName(bullet.getPlayer().getPlayerName());
							hitMessage.setPlayerLocation(bullet.getPlayer().getCameraPosition());
							hitMessage.setDamage(damage);
							hitMessage.setWeaponFlag(NewBulletMessage.PISTOLGUN);
							((Server) syncManager).getServer().sendToPlayer(hitMessage, opp.getPlayerId());
						}
					}
					
					//bullet.remove();
					bullet.setActive(false);
					
					return ; // Abort
				}
			}
			
			// Check Vehicle
			Vector<Spatial> vehicles = CollidableObjects.getInstance().getObjects("vehicle");
			if(vehicles != null) {
				for(Spatial vec:vehicles) {
					if(bullet.getPlayer() != null && bullet.getPlayer().hasVehicle() && bullet.getPlayer().getVehicle() == vec) break;
					if(vec != null && vec.hasCollision(bullet, false)) {
						doExplosion();
		
						
						((Vehicle)vec).setDamage(((Vehicle)vec).getDamage()+8, true);
						
						bullet.setActive(false);
						
						return ; // Abort
					}
				}
			}
		}
	}
	
	
	public void calculateMaxDistance() {
		maxDistance = GlobalSettings.MAXBULLETDISTANCE;
		
		// Calculate Distance to travel until it hits a wall/ground
		// Get New Location
		Ray ray = new Ray(bullet.getStartPosition(), direction);
		PickResults results = new TrianglePickResults();
		results.setCheckDistance(true);
		
		// person.getParent() is the rootNode!
		TerrainBlock tb = ((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getTerrainBlock();
		int size = (tb == null) ? 3:4;
		Spatial[] checkSpatials = new Spatial[size];
		checkSpatials[0] = CollidableObjects.getInstance().getObjects("wallNode").get(0);
		checkSpatials[1] = CollidableObjects.getInstance().getObjects("groundNode").get(0);
		checkSpatials[2] = CollidableObjects.getInstance().getObjects("objNode").get(0);
		if(tb != null) checkSpatials[3] = tb;
		
		
		
		for(int i=0;i<checkSpatials.length;i++) {
			checkSpatials[i].findPick(ray,results);
			
			//check if we hit something
			if(results.getNumber() > 0) {
			   PickData closest = results.getPickData(0);
			  
			   
			   if(maxDistance > closest.getDistance()) {
				   maxDistance = closest.getDistance();
			   }

			}
		}
	}
	
	private void doExplosion() {
		ParticleEffects.getInstance().getParticleSystem("hit").setStartColor(bullet.getBulletColor());
		ParticleEffects.getInstance().getParticleSystem("hit").setEndColor(bullet.getBulletColor());
		
		ParticleEffects.getInstance().getParticleNode("hit")
			.setLocalTranslation(bullet.getLocalTranslation());
		ParticleEffects.getInstance().getParticleSystem("hit")
			.setEmissionDirection(this.direction.mult(new Vector3f(-1,0,-1)));
		ParticleEffects.getInstance().getParticleNode("hit")
			.updateGeometricState(0, false);
		ParticleEffects.getInstance().getParticleSystem("hit").forceRespawn();
	}

	public Vector3f getDirection() {
		return direction;
	}

	public void setDirection(Vector3f direction) {
		this.direction = direction;
	}
	
	public void setDamage(int top, int middle, int bottom) {
		topthirdDamage = top;
		middlethirdDamage = middle;
		bottomthirdDamage = bottom;
	}
}